home *** CD-ROM | disk | FTP | other *** search
- #include <stdio.h>
- #include <bios.h>
- #include <conio.h>
- #include <dir.h>
- #include <dos.h>
- #include <io.h>
- #include <mem.h>
- #include <stdlib.h>
- #include <string.h>
- #include <ctype.h>
- #include <alloc.h>
-
- #ifdef toupper
- #undef toupper /* don't want a macro version with side effects */
- #endif
-
- /****************************** Warning !!!! ******************************
-
- This program uses writes to absolute sectors for copying and can format
- disk tracks. If you make changes, be sure that you know what you are
- doing. In particular, note that if a secondary floppy controller is in
- use, the drive numbers used by int 13h for drives other than A: and B:
- depend on the device driver used to access that controller. On my system,
- the two floppies on my secondary controller are H: and I:, yet they respond
- as 4 and 5 to INT 13h.
-
- ****************************************************************************/
-
- /* List of possible enhancements
- 1. Insert a proper volume serial number (different from the original).
- Ignore this difference when verifying.
- 2. Allow output drive != input.
- 3. Configure colors.
- 4. Use extended or expanded memory rather than a disk file.
- */
-
- #define LOGICAL int
- #define FALSE 0
- #define TRUE 1
- #define START_CLOCK 0
- #define READ_CLOCK 1
- #define ERR -1
- #define NO_ERR 0
-
- #define BACKGROUND_COLOR BLUE
- #define TEXT_COLOR WHITE
- #define READ_INDICATOR RED
- #define FORMAT_INDICATOR MAGENTA
- #define WRITE_INDICATOR GREEN
- #define VERIFY_INDICATOR YELLOW
-
- #define BYTES_PER_SECTOR 512
-
- #define MAX_SECTORS 60
- #define BUFFER_SIZE MAX_SECTORS*512
-
- #define ERROR_ALERT() {sound(45); delay(500); nosound();}
- #define UP_N(n) {gotoxy(1, wherey() - n);}
- #define UP_ONE_AND_CLEAR() \
- {cprintf("\r"); clreol(); gotoxy(1, wherey() - 1); clreol();}
-
- /* biosdisk() BIOS function cmd calls */
-
- #define RESET_CONTROLLER 0
- #define READ_TRK 2
- #define WRITE_TRK 3
- #define VERIFY_TRK 4
- #define FORMAT_TRK 5
-
- #define FLOPPY_360K 0
- #define FLOPPY_12MB 1
- #define FLOPPY_720K 2
- #define FLOPPY_144M 7
-
- /* Prototypes for local routines
- */
- char *getvolid(int dos_drive);
- void far hardset(void);
- void measure_speed(int function, char *thing_timed);
- void statline(int sectors, int color);
- int disk_format(int int13_drive, int drive_type);
- void erase_temp_file(void);
- LOGICAL disk_rw(int int13_drive, int action, int first_sector, int nsects,
- char *buffer);
- void reset_disk_controller(void);
- void disk_error_print(unsigned int error_code);
-
- /* wsscopyd - copy a disk using another disk for temporary storage of
- disk image.
- usage: WSScopyd [options] d[:] [options]
-
- Options (case ignored, / or - switchchar allowed):
- -B - beep when disk change is needed
- -Cn - number of copies
- -F - format target disk
- -R fn - reuse saved file fn
- -S fn - save disk file with name fn
- -T - display time used
- -V - verify by reading floppy and comparing to original data
- -X1 - disk is single-sided, 10 sectors per track, 80 tracks
- -X2 - disk is double-sided, 10 sectors per track, 80 tracks
- -X3 - disk is RX-50 (like X1 but tracks after 1st two are 2:1 interleave)
- -X format source disks do not need to have a valid boot sector.
- Volume label will not be displayed during copying when -X is
- specified.
-
- Compile with compact or large model else allocation of buffers will fail.
-
- DESQView compatibility: DOS Buffers for EMS should be set larger than the
- default value of 2K else run time will be excessive. 4K is the minimum
- acceptable value, but 8K is better. Performance continues to improve
- up to at least 32K. The -t option can be used to time operation with
- different values. The following read times were obtained on a 386/25:
-
- EMS buffer size Seconds
- 2K 307.8 (default setting)
- 4K 161.6
- 8K 93.5
- 16K 76.3
- 32K 68.4
- under DOS 59.4
-
- All screen output leaves a 2-character margin on left and right so that
- everything will be visible even if there is a window border at the edge
- of the screen. The right margin also eliminates any worry about whether
- writing in the last character position of the bottom row causes the screen
- to scroll.
- */
-
- volatile int Hard_error; /* Set to TRUE by critical error handler. Note
- that any call to absread or abswrite can
- potentially set Hard_error, even if the
- call succeeds after retrying. */
- int Fail_ignore = 3; /* tell critical error handler to fail on error */
- int Tr_per_dsk = 40;
- int Sd_per_dsk = 1;
- int Sectors_per_track = 8;
- int Sectors_per_disk;
- LOGICAL Is_X;
- int X_type;
- void interrupt (*System_dpb)();
-
- struct DPB
- {
- char spec1; /* first specify byte */
- char spec2; /* second specify byte */
- char stop_delay; /* delay until motor turned off (ticks) */
- char bps; /* bytes per sector 0=128, 1=256, 2=512, 3=1024 */
- char spt; /* sectors per track */
- char gap; /* gap between sectors (2Ah for 5.25", 1Bh for 3.5") */
- char data_l; /* data length (ignored if bps is non-zero) */
- char gap_l; /* formatting gap length (50h for 5.25", 6ch for 3.5") */
- char filler; /* format filler byte (default F6h) */
- char settle; /* head settle time in milliseconds */
- char start_time; /* motor start time in 1/8 seconds */
- } my_dpb;
-
- char Tempfile[100]; /* file name for storage disk to be copied, global
- so that exit routine can erase it if needed */
-
- void main(int argc, char **argv)
- {
- char huge *buffer, /* buffer for data from floppy or save file */
- huge *buffer2, /* buffer for data from floppy when verifying */
- *cfg_path, /* full path to configuration file */
- *cmp1, /* pointer looping through buffer */
- *cmp2, /* pointer looping through buffer2 */
- *openmode, /* mode for opening disk file, read or update */
- *temp_path, /* TEMP or TMP environment variable */
- *volname; /* volume name or <none> */
- FILE *fp; /* file pointer for save file */
- int disk_cap, /* disk drive capacity from cfg file, 1=360k, etc */
- dos_drive, /* drive number for DOS calls, A=0, B=1,... */
- drive_type, /* current drive type */
- fat_sub, /* 0 or 1 index into FAT mask and test arrays */
- first_sec, /* first sector to be read from floppy */
- i, /* for loop index */
- icopy, /* counter for number of copies made */
- int13_drive, /* drive number for int 13h calls */
- key, /* key hit in response to a prompt */
- max_sectors_use, /* number of sectors used in read/write buffer,
- a multiple of the number of sectors per track */
- n_alloc, /* number of allocation units addressed by FAT */
- ncopies, /* number of copies requested */
- ndrives, /* number of drives defined in config file */
- nread, /* number of sectors to read in one call */
- nver, /* number of sectors to verify in one call */
- nwrite, /* number of sectors to write in one call */
- status, /* status from ioctl check for removability or
- from disk_rw call */
- tot_sects; /* number of sectors on source disk */
- unsigned int j, /* for loop index */
- nbytes; /* number of bytes to verify on one disk read */
- LOGICAL format, /* if TRUE, format disks */
- have_drive, /* set TRUE if command line specified drive */
- save_temp_file,/* if TRUE, temp file is saved */
- time, /* if TRUE, display time used by each operation */
- use_beep, /* if TRUE, sound alarm when disk swap is needed */
- use_old_file, /* if TRUE, use an old temp file */
- verify; /* if TRUE, verify copy by re-reading and comparing
- with what should have been written */
- char beep; /* control-g if alarm enabled, blank if not */
- char cfg_load_path[MAXPATH], /* program load path with wsscopyd.cfg
- appended */
- disk_letter[80], /* scanf buffer for disk letter read */
- disk_type[80], /* scanf buffer for disk type read */
- int13_number[80], /* scanf buffer for int 13h disk number */
- load_dir[MAXDIR], /* directory program was loaded from */
- load_drive[MAXDRIVE]; /* drive program was loaded from */
-
- unsigned int tseg;
-
- struct BPB /* info in floppy boot sector */
- {
- char jump[3];
- char oem[8];
- short bytes_per_sector;
- char sectors_per_alloc;
- short reserved_secs;
- char num_fats;
- short num_root_dir;
- short total_sectors;
- char media_desc;
- short sectors_per_fat;
- short sectors_per_track;
- short num_heads;
- short num_hidden;
- } huge *bpb;
-
- struct
- {
- int number;
- int type;
- char letter;
- } disk_table[26];
-
- short *fat_12; /* used for indexing through 12-bit FAT */
- int fat12_mask[2] = {0xfff0, 0xfff}; /* masks to select one FAT entry */
- int fat12_test[2] = {0xff70, 0xff7}; /* bad block FAT values */
-
- directvideo = 0; /* use ROM BIOS, should avoid bleedthrough in DV
- windows on pre-386 CPU's */
-
- textbackground(BACKGROUND_COLOR);
- textcolor(TEXT_COLOR);
- cprintf("\r\n WSScopyd version 1.1\r\n"
- " Copyright December 6, 1992 "
- "by Robert W. Babcock and WSS Division of DDC\r\n"
- " Unlimited non-commercial use authorized\r\n\n\n");
-
- /* Need DOS 3+ for removability check
- */
- if(_osmajor < 3)
- {
- cprintf(" Sorry, WSScopyd requires DOS 3 or higher\r\n");
- exit(1);
- }
-
- /* Read configuration file. First look in directory program was loaded from,
- then try current directory and path.
- */
- fnsplit(argv[0], load_drive, load_dir, NULL, NULL);
- fnmerge(cfg_load_path, load_drive, load_dir, "WSSCOPYD", "CFG");
- if(0 == access(cfg_load_path, 0))
- cfg_path = cfg_load_path;
- else if(NULL == (cfg_path = searchpath("WSSCOPYD.CFG")))
- {
- cprintf(" Configuration file WSSCOPYD.CFG must be in current directory "
- "or on path\r\n"
- " See documentation file for format of this file\r\n");
- exit(1);
- }
- if(NULL == (fp = fopen(cfg_path, "r")))
- {
- cprintf(" %s cannot be opened\r\r", cfg_path);
- exit(1);
- }
- /* Skip over comments at beginning of configuration file
- */
- for(;;)
- {
- if(NULL == fgets(Tempfile, 99, fp))
- break;
- if(0 == strncmp(Tempfile, "----------", 10))
- break;
- }
- for(ndrives=0; ndrives<26 ; ndrives++)
- {
- if(3 != fscanf(fp, "%s %s %s", &disk_letter, &int13_number, &disk_type))
- break;
- disk_table[ndrives].letter = *disk_letter;
- disk_table[ndrives].number = atoi(int13_number);
- if(0 == strncmp(disk_type, "360", 3))
- disk_cap = FLOPPY_360K;
- else if(0 == strncmp(disk_type, "12", 2))
- disk_cap = FLOPPY_12MB;
- else if(0 == strncmp(disk_type, "1.2", 3))
- disk_cap = FLOPPY_12MB;
- else if(0 == strncmp(disk_type, "72", 2))
- disk_cap = FLOPPY_720K;
- else if(0 == strncmp(disk_type, "144", 3))
- disk_cap = FLOPPY_144M;
- else if(0 == strncmp(disk_type, "1.44", 4))
- disk_cap = FLOPPY_144M;
- else
- {
- cprintf(" Configuration file error\r\n"
- " Floppy drive %c, number %d, type %s not recognized\r\n",
- disk_table[ndrives].letter, disk_table[ndrives].number,
- disk_type);
- exit(1);
- }
- disk_table[ndrives].type = disk_cap;
- }
- fclose(fp);
-
- if(0 == ndrives)
- {
- cprintf(" No drive info readable in %s\r\n", cfg_path);
- exit(1);
- }
-
- if(argc < 2)
- {
- cprintf(" Usage: WSScopyd [options] drive: [options]\r\n"
- " Options are marked by / or -\r\n"
- " -B - beep when disk change is needed\r\n"
- " -Cn - number of copies\r\n"
- " -F - format target disk\r\n"
- " -R fn - reuse saved file fn\r\n"
- " -S fn - save disk file with name fn\r\n"
- " -T - display time used\r\n"
- " -V - verify by reading floppy and comparing to original data\r\n"
- " -X1 - disk is single-sided, 10 sectors per track, 80 tracks\r\n"
- " -X2 - disk is double-sided, 10 sectors per track, 80 tracks\r\n"
- " -X3 - disk is RX-50\r\n");
- exit(1);
- }
-
- /* Alignment of bpb, buffer and (later) buffer2 are adjusted so that they
- do not cross a 64K boundary. This avoids problems with int 13h in some
- environments, particularly under OS/2. I think that int 13h never likes
- buffers crossing 64K, but the BIOS usually fixes things up for you.
- */
- bpb = (struct BPB *)farmalloc(1024);
- tseg = FP_SEG(bpb)+FP_OFF(bpb)/16;
- if((0xf000 & (tseg+512/16)) != (0xf000 & tseg))
- bpb += 512;
- if(NULL == (buffer = farmalloc(2L*BUFFER_SIZE)))
- {
- cprintf(" Error: out of memory\r\n");
- exit(1);
- }
- tseg = FP_SEG(buffer)+FP_OFF(buffer)/16;
- if((0xf000 & (tseg+BUFFER_SIZE/16)) != (0xf000 & tseg))
- buffer += BUFFER_SIZE;
-
- /* Parse command line
- */
- use_beep = format = time = verify = Is_X = FALSE;
- have_drive = save_temp_file = use_old_file = FALSE;
- ncopies = 1;
- for(argc--, argv++ ; argc>0; argc--, argv++)
- {
- if('-' == **argv || '/' == **argv)
- {
- (*argv)++;
- switch(toupper(**argv))
- {
- case 'B':
- use_beep = TRUE;
- continue;
-
- case 'C':
- (*argv)++;
- if('\0' == **argv)
- {
- argv++;
- argc--;
- if(0 == argc)
- {
- cprintf(" Error, number of copies missing\r\n");
- exit(1);
- }
- }
- ncopies = atoi(*argv);
- continue;
-
- case 'F':
- format = TRUE;
- continue;
-
- case 'R':
- use_old_file = TRUE;
- (*argv)++;
- if('\0' == **argv)
- {
- argv++;
- argc--;
- if(0 == argc)
- {
- cprintf(" Error, old save file name missing\r\n");
- exit(1);
- }
- }
- strcpy(Tempfile, *argv);
- continue;
-
- case 'S':
- save_temp_file = TRUE;
- (*argv)++;
- if('\0' == **argv)
- {
- argv++;
- argc--;
- if(0 == argc)
- {
- cprintf(" Error, save file name missing\r\n");
- exit(1);
- }
- }
- strcpy(Tempfile, *argv);
- continue;
-
- case 'T':
- time = TRUE;
- continue;
-
- case 'V':
- verify = TRUE;
- continue;
-
- case 'X':
- Is_X = TRUE;
- (*argv)++;
- if('\0' == **argv)
- {
- argv++;
- argc--;
- if(0 == argc)
- {
- cprintf(" Error, -X should be followed by 1,2 or 3\r\n");
- exit(1);
- }
- }
- X_type = atoi(*argv);
- if(X_type < 1 || X_type > 3)
- {
- cprintf(" Unrecognized Xn option\r\n");
- exit(1);
- }
- continue;
-
- default:
- cprintf(" Unrecognized option %s, "
- "run with no arguments for help\r\n", *argv);
- exit(1);
- }
- }
- else
- {
- if(have_drive)
- {
- cprintf(" Error, missing switchchar or drive specified twice\r\n");
- exit(1);
- }
- else
- {
- dos_drive = toupper(**argv);
- have_drive = TRUE;
- }
- }
- }
-
- /* Check for valid options requested
- */
- if(!have_drive)
- {
- cprintf(" Error, drive not specified\r\n");
- exit(1);
- }
-
- if(ncopies < 0 || ((0 == ncopies) && !save_temp_file))
- {
- cprintf(" Error, requested number of copies is %d\r\n", ncopies);
- exit(1);
- }
-
- for(i=0; i<ndrives; i++)
- {
- if(dos_drive == disk_table[i].letter)
- {
- int13_drive = disk_table[i].number;
- drive_type = disk_table[i].type;
- goto found_drive_info;
- }
- }
- cprintf(" Drive %c does not appear in WSSCOPYD.CFG\r\n", dos_drive);
- exit(1);
-
- /* Kill the high bit of format_drive to avoid the possibility of it pointing
- at a hard disk due to an error in specifying drive.
- */
- found_drive_info:
- int13_drive &= 0x7f;
- dos_drive -= 'A';
-
- if(save_temp_file && use_old_file)
- {
- cprintf(" Error, -R and -S options are mutually exclusive\r\n");
- exit(1);
- }
-
- beep = use_beep ? '\a' : ' ';
-
- if(verify)
- {
- if(NULL == (buffer2 = farmalloc(2L*BUFFER_SIZE)))
- {
- cprintf(" Error: not enough memory for verify buffer\r\n");
- exit(1);
- }
- tseg = FP_SEG(buffer2)+FP_OFF(buffer2)/16;
- if((0xf000 & (tseg+BUFFER_SIZE/16)) != (0xf000 & tseg))
- buffer2 += BUFFER_SIZE;
- }
-
- /* Make sure the disk to be copied is removable
- */
- status = ioctl(dos_drive+1, 8, 0, 0);
- if(0 != status)
- {
- cprintf(" Error: WSScopyd only works for removable media\r\n");
- exit(1);
- }
-
- if(!(save_temp_file || use_old_file))
- {
- temp_path = getenv("TEMP");
- if(NULL == temp_path)
- temp_path = getenv("TMP");
- if(NULL == temp_path)
- {
- cprintf(" Error: environment variable TEMP or TMP must be defined\r\n");
- exit(1);
- }
- strcpy(Tempfile, temp_path);
- if('\\' != Tempfile[strlen(Tempfile)-1])
- strcat(Tempfile, "\\");
- strcat(Tempfile, "copyd.$$$");
- }
-
- /* Set up critical error handler. DOS undoes this automatically when we exit.
- */
- hardset();
-
- /* Start the main copy loop
- */
- do_another:
- if(!use_old_file)
- {
- if(time)
- measure_speed(START_CLOCK, "");
-
- if(0 != (status = disk_rw(int13_drive, READ_TRK, 0, 1, (char *)bpb)))
- {
- ERROR_ALERT();
- cprintf("\r\n Error: unable to read floppy boot sector\r\n");
- disk_error_print(status);
- exit(1);
- }
- if( !Is_X && (512 != bpb->bytes_per_sector))
- {
- ERROR_ALERT();
- cprintf("\r\n Error: floppy boot sector indicates other than 512 "
- "byte sectors\r\n");
- exit(1);
- }
- if(Is_X)
- volname = "????";
- else
- volname = getvolid(dos_drive + 1);
- }
-
- openmode = use_old_file ? "rb" : "w+b";
- if(NULL == (fp = fopen(Tempfile, openmode)))
- {
- ERROR_ALERT();
- cprintf("\r\n Error: unable to open temporary file %s\r\n", Tempfile);
- exit(1);
- }
- if(!(use_old_file || save_temp_file))
- atexit(erase_temp_file);
-
- /* Read floppy and copy to temporary file
- */
- if(use_old_file)
- {
- Hard_error = FALSE;
- if(1 != fread(bpb, sizeof(struct BPB) + 12, 1, fp)
- || 1 != fread(buffer, 512 - 12 - sizeof(struct BPB), 1, fp)
- || Hard_error)
- {
- cprintf("\r\n Error reading saved file %s\r\n", Tempfile);
- exit(1);
- }
- volname = (char *)bpb + sizeof(struct BPB);
- }
-
- if(Is_X)
- {
- Sd_per_dsk = 2 == X_type ? 2 : 1;
- Tr_per_dsk = 80;
- Sectors_per_track = 10;
- Sectors_per_disk = Sd_per_dsk * Tr_per_dsk * Sectors_per_track;
- }
- else
- {
- Sectors_per_disk = bpb->total_sectors;
- Sd_per_dsk = bpb->num_heads;
- Tr_per_dsk = Sectors_per_disk / (Sd_per_dsk * bpb->sectors_per_track);
- Sectors_per_track = bpb->sectors_per_track;
- }
-
- /* Select buffer size which is a multiple of the number of sectors per
- track * number of sides.
- */
- max_sectors_use = (MAX_SECTORS/(Sectors_per_track*Sd_per_dsk))
- *Sectors_per_track*Sd_per_dsk;
- if(0 == max_sectors_use)
- max_sectors_use = min(MAX_SECTORS, Sectors_per_track);
-
- if(!use_old_file)
- {
- first_sec = 0;
- tot_sects = Sectors_per_disk;
- cprintf("\r Reading %d sectors, Volume %s, making %d copies...",
- tot_sects, volname, ncopies);
- clreol();
- statline(-tot_sects, 0);
- memset(buffer, 0, 512);
- Hard_error = FALSE;
- if(1 != fwrite(bpb, sizeof(struct BPB), 1, fp)
- || 1 != fwrite(volname, 12, 1, fp)
- || 1 != fwrite(buffer, 512 - 12 - sizeof(struct BPB), 1, fp)
- || Hard_error)
- {
- ERROR_ALERT();
- cprintf("\r\n Error: temp disk write failed\r\n");
- exit(1);
- }
-
- while(tot_sects > 0)
- {
- nread = min(max_sectors_use, tot_sects);
- if(0 != (status = disk_rw(int13_drive, READ_TRK, first_sec, nread, (char *)buffer)))
- {
- ERROR_ALERT();
- cprintf("\r\n Error: floppy read failed\r\n");
- disk_error_print(status);
- exit(1);
- }
- /* On first pass through here, check that the FAT indicates no bad sectors
- on master disk. (Can't do this for special formats since location of FAT
- is unknown.)
- */
- if(0 == first_sec && !Is_X)
- {
- n_alloc = Sectors_per_disk / bpb->sectors_per_alloc;
- if(n_alloc < 4087)
- {
- /* 12-bit FAT
- */
- fat_12 = (short *)(buffer + bpb->reserved_secs * 512 + 3);
- for(i=fat_sub=0; i<n_alloc; i++, fat_12 += 1 + fat_sub, fat_sub = 1 - fat_sub)
- {
- if((*fat_12 & fat12_mask[fat_sub]) != fat12_test[fat_sub])
- continue;
- ERROR_ALERT();
- cprintf("\r\n Error: floppy has bad sectors marked in FAT"
- "\r\n WSScopyd only copies perfect disks\r\n");
- cprintf("\r\ni = %d, fat = %x\r\n", i, *fat_12);
- exit(1);
- }
- }
- else
- {
- /* 16-bit FAT (don't expect this code to ever be executed)
- */
- ERROR_ALERT();
- cprintf("\r\n Sorry, I don't know how to handle 16-bit "
- "FAT's\r\n");
- exit(1);
- }
- }
- tot_sects -= nread;
- first_sec += nread;
- statline(tot_sects, READ_INDICATOR);
- if(nread != fwrite(buffer, BYTES_PER_SECTOR, nread, fp))
- {
- ERROR_ALERT();
- cprintf("\r\n Error: temp disk write failed\r\n");
- exit(1);
- }
- }
-
- /* Read first sector again to position drive head
- */
- disk_rw(int13_drive, READ_TRK, 0, 1, (char *)buffer);
-
- if(time)
- {
- measure_speed(READ_CLOCK, "Reading source disk");
- cprintf("\n");
- }
- }
-
- /* Done reading, ready to copy
- */
- for(icopy=0; icopy<ncopies; icopy++)
- {
- wait_for_blank:
- UP_ONE_AND_CLEAR();
- cprintf(" Waiting for blank disk, hit a key...%c", beep);
- restart_after_error:
- Hard_error = FALSE;
- fseek(fp, 512, SEEK_SET);
- key = getch();
- if(3 == key || 0x1b == key)
- {
- fclose(fp);
- cprintf("\r\n\n");
- exit(0);
- }
-
- if(time)
- measure_speed(START_CLOCK, "");
-
- if(format)
- {
- tot_sects = Sectors_per_disk;
- cprintf("\r Formatting %d sectors, Volume %s, copy number "
- "%d of %d...", Sectors_per_disk, volname, icopy+1, ncopies);
- clreol();
- statline(-2*Tr_per_dsk, 0);
-
- if(ERR == disk_format(int13_drive, drive_type))
- {
- ERROR_ALERT();
- UP_ONE_AND_CLEAR();
- cprintf("\r\n Error: floppy format failed, "
- "insert replacement disk...");
- goto restart_after_error;
- }
-
- if(time)
- {
- measure_speed(READ_CLOCK, "Formatting disk");
- cprintf("\n");
- measure_speed(START_CLOCK, "");
- }
- UP_ONE_AND_CLEAR();
- }
- else if(!Is_X)
- {
- /* Preformatted disk, read boot sector and make sure it is compatible.
- Also check that there are no bad sectors marked in the FAT. (Can't do
- this for special formats since location of FAT is unknown.)
- */
- if(0 != (status = disk_rw(int13_drive, READ_TRK, 0,
- 1 + bpb->sectors_per_fat, (char *)buffer)))
- {
- ERROR_ALERT();
- cprintf("\r\n Error: can't read boot sector, insert formatted "
- "disk...\r\n");
- disk_error_print(status);
- UP_N(3);
- goto restart_after_error;
- }
- if(0 != memcmp(buffer+11, &bpb->bytes_per_sector, sizeof(struct BPB)-11))
- {
- ERROR_ALERT();
- cprintf("\r\n Error: target disk is not the same format as "
- "master, insert replacement...\r\n\n");
- goto restart_after_error;
- }
- n_alloc = Sectors_per_disk / bpb->sectors_per_alloc;
- fat_12 = (short *)(buffer + bpb->reserved_secs * 512 + 3);
- for(i=fat_sub=0; i<n_alloc; i++, fat_12 += 1 + fat_sub, fat_sub = 1 - fat_sub)
- {
- if((*fat_12 & fat12_mask[fat_sub]) != fat12_test[fat_sub])
- continue;
- ERROR_ALERT();
- cprintf("\r\n Error: target floppy has bad sectors marked in "
- "FAT, insert replacement...\r\n\n");
- goto restart_after_error;
- }
- }
- tot_sects = Sectors_per_disk;
- cprintf("\r Writing %d sectors, Volume %s, copy number "
- "%d of %d...", tot_sects, volname, icopy+1, ncopies);
- clreol();
- first_sec = 0;
- statline(-tot_sects, 0);
- while(tot_sects > 0)
- {
- nwrite = min(max_sectors_use, tot_sects);
- Hard_error = FALSE;
- if(nwrite != fread(buffer, BYTES_PER_SECTOR, nwrite, fp)
- || Hard_error)
- {
- ERROR_ALERT();
- cprintf("\r\n Error: temp disk read failed\r\n");
- exit(1);
- }
-
- if(0 != (status = disk_rw(int13_drive, WRITE_TRK, first_sec, nwrite,
- (char *)buffer)))
- {
- ERROR_ALERT();
- UP_ONE_AND_CLEAR();
- cprintf("\r\n Error: floppy write failed, insert "
- "replacement...\r\n");
- disk_error_print(status);
- UP_N(3);
- goto restart_after_error;
- }
- tot_sects -= nwrite;
- first_sec += nwrite;
- statline(tot_sects, WRITE_INDICATOR);
- }
-
- if(time)
- {
- measure_speed(READ_CLOCK, "Writing disk");
- cprintf("\n");
- }
-
- /* Optionally verify by reading floppy and temp file and comparing
- */
- if(verify)
- {
- if(time)
- measure_speed(START_CLOCK, "");
- fseek(fp, 512, SEEK_SET);
- tot_sects = Sectors_per_disk;
- cprintf("\r"); clreol(); UP_N(1);
- cprintf(" Verifying %d sectors, Volume %s, "
- "copy number %d of %d...",
- tot_sects, volname, icopy+1, ncopies);
- clreol();
- first_sec = 0;
- statline(-tot_sects, 0);
- Hard_error = FALSE;
- while(tot_sects > 0)
- {
- nver = min(max_sectors_use, tot_sects);
- Hard_error = FALSE;
- if(nver != fread(buffer, BYTES_PER_SECTOR, nver, fp)
- || Hard_error)
- {
- ERROR_ALERT();
- cprintf("\r\n Error: temp disk read failed during verify\r\n");
- exit(1);
- }
- if(0 != (status = disk_rw(int13_drive, READ_TRK, first_sec, nver,
- (char *)buffer2)))
- {
- ERROR_ALERT();
- UP_ONE_AND_CLEAR();
- cprintf("\r\n Error: floppy read failed during verify, "
- "insert replacement...\r\n");
- disk_error_print(status);
- UP_N(3);
- goto restart_after_error;
- }
- nbytes = nver * BYTES_PER_SECTOR;
- cmp1 = (char *)buffer;
- cmp2 = (char *)buffer2;
- for(j=0; j<nbytes; j++)
- {
- if(*cmp1++ != *cmp2++)
- {
- ERROR_ALERT();
- UP_ONE_AND_CLEAR();
- cprintf("\r\n Disk verify failed with unequal compare, "
- "insert replacement...");
- UP_N(1);
- goto restart_after_error;
- }
- }
- tot_sects -= nver;
- first_sec += nver;
- statline(tot_sects, VERIFY_INDICATOR);
- }
- if(time)
- {
- measure_speed(READ_CLOCK, "Verifying disk");
- cprintf("\n");
- }
- }
- }
-
- fclose(fp);
- if(save_temp_file)
- {
- cprintf("\r\n Disk image saved in file %s\r\n", Tempfile);
- exit(0);
- }
- if(use_old_file)
- {
- cprintf("\r\n %d copies completed\r\n", ncopies);
- exit(0);
- }
- UP_ONE_AND_CLEAR();
- cprintf(" Waiting for source disk, hit a key...%c", beep);
- key = getch();
- if(3 == key || 0x1b == key)
- {
- cprintf("\r\n\n");
- exit(0);
- }
- goto do_another;
- }
-
- /*********************************************************************
- statline - print initial or updated progress indicator
- Initialize with sectors = -total number of sectors on disk (or whatever is
- being counted). Initialization call also clears the line below the
- progress indicator. On calls to update progress indicator, also check for
- any keystrokes. Exit on ^C or escape, flush anything else.
- */
- void statline(int sectors, int color)
- {
- int count, i;
- static int old_count, total_sectors;
-
- if(sectors < 0)
- {
- cprintf("\r\n\n");
- clreol();
- gotoxy(3, wherey()-1);
- for(i=0; i<37; i++)
- cprintf("\304\305"); /* special chars are -- and -|- */
- cprintf("\304\264\r "); /* special chars are -- and -| */
- old_count = 0;
- total_sectors = -sectors;
- }
- else
- {
- count = (int)((76L*(total_sectors - sectors))/total_sectors);
- textcolor(color);
- for(i=old_count; i<count; i++)
- cprintf("\333"); /* special char is solid block */
- textcolor(TEXT_COLOR);
- old_count = count;
- while(kbhit())
- {
- switch(getch())
- {
- case 3: /* control-c */
- case 0x1b: /* escape */
- cprintf("\r\n Aborting\r\n");
- exit(1);
-
- default:
- continue;
- }
- }
- }
- }
-
- /*********************************************************************
- measure_speed - measure how much time elapsed between successive calls
- Start timing by calling with function=0, end timing with function=1
- and thing_timed a descriptive string. After a type 1 call, the start
- time is preserved so that cummulative timing can be done.
- */
- #include <sys/timeb.h>
- void measure_speed(int function, char *thing_timed)
- {
- static struct timeb start;
- struct timeb finish;
- long seconds;
- short ms;
-
- switch(function)
- {
- case START_CLOCK:
- ftime(&start);
- break;
-
- case READ_CLOCK:
- ftime(&finish);
- seconds=finish.time-start.time;
- ms=finish.millitm-start.millitm;
- if(ms < 0)
- {
- seconds--;
- ms += 1000;
- }
- cprintf("\r %s took %ld.%03hd seconds", thing_timed, seconds, ms);
- clreol(); cprintf("\r\n");
- break;
- }
- return;
- }
-
- /*********************************************************************
- getvolid - get volume label from disk to be copied, return in static
- variable. Arg is drive number (0=A,1=B,...)
- */
- struct dos_fcb {
- unsigned char flag; /* ff to indicate extended fcb */
- char res[5]; /* reserved */
- unsigned char vattr; /* attribute */
- unsigned char drive; /* drive, 1=A, 2=B,... */
- unsigned char vn[11]; /* file or volume name */
- unsigned char fattr; /* attributes of found file */
- char resrvd[10]; /* reserved */
- unsigned int time; /* last write time */
- unsigned int date; /* last write date */
- long size; /* size of file */
- };
- char *getvolid(int dos_drive)
- {
- struct dos_fcb fcb1, fcb2;
- static char volid[12];
- struct SREGS sregs;
- union REGS regs;
-
- setdta((char far *)&fcb1);
- memset(&fcb2, 0, sizeof(struct dos_fcb));
- sregs.ds = FP_SEG(&fcb2);
- fcb2.flag = 0xff;
- fcb2.vattr = 8;
- fcb2.drive = dos_drive;
- strncpy((char *)fcb2.vn, "???????????", 11);
- regs.x.dx = FP_OFF(&fcb2);
- regs.h.ah = 0x11; /* search for first entry */
- intdosx(®s, ®s, &sregs);
- if(regs.h.al)
- strcpy(volid, "<none>");
- else
- strncpy(volid, (char *)fcb1.vn, 11);
- volid[11] = '\0';
- return(volid);
- }
-
- /*********************************************************************
- Routine disk_format is derived from ZFMAT.C by Edward V. Dong.
- That code did not work for 360K in 1.2M under DOS 5, nor did it handle
- 1.2M or 3.5" disks.
- */
-
- /* ----------------------------------------------------------------
-
- Copyright 1988 by Edward V. Dong, All Rights Reserved.
-
- This source code is placed into the public domain. However,
- contributions are always welcome. This is a modified form of the
- source code used in the author's ZIP program.
-
- Edward V Dong's Floppy Formattor is a Turbo C program to format
- floppy diskettes, based on earlier work by Frank Nystrom
- (COPYIT.C;CIS 71631,355), Kim Kokkonen (FMAT.PAS; Compuserve
- 72457,2131), and Peter Norton (BOOT.ASM). This programs formats,
- accurately, 360K diskettes in either standard 360K drives or 1.2M
- drives.
-
- To format 360K diskettes in 1.2 megabyte drives, the DASD must be
- set - this is only possible for DOS 3.0 or higher and for AT class
- machines. The routines here are based on Kokkonen's Turbo Pascal
- work, ported to Turbo C.
-
- Edward V. Dong, 12205 Alexandria Pl, Chino, CA 91710. 18 May 1988
-
- ----------------------------------------------------------------*/
-
- /* Disk Constants...*/
-
- #define MAX_SECTORS_PER_TRACK 18
-
- #define FORMAT_360K 1
- #define FORMAT_12MB 2
- #define FORMAT_720K 3
- #define FORMAT_144M 4
-
- #define FORMAT_X 99
-
- /* When you initialize the disk you need to pass the bios some things.
- */
-
- struct
- {
- char cyl,
- head,
- rec,
- num;
- } format_record[MAX_SECTORS_PER_TRACK];
-
- /*********************************************************************
- disk_format - this does the formatting
- */
- int disk_format(int int13_drive, int drive_type)
- {
- register int tr, sd;
- int s, tries, status, success;
- int format_type, master_type;
- LOGICAL call_17h;
-
- /* Get and save address of old disk parameter block, copy to a replacement
- block (fields will be changed later). Restore the old block when we
- terminate.
- */
- System_dpb = getvect(0x1e);
- movedata(FP_SEG(System_dpb), FP_OFF(System_dpb),
- FP_SEG(&my_dpb), FP_OFF(&my_dpb), sizeof(struct DPB));
- setvect(0x1e, (void interrupt (*)()) &my_dpb);
- atexit(reset_disk_controller);
-
- status = ERR;
-
- if(Is_X)
- master_type = FORMAT_X;
- else if(Tr_per_dsk < 80)
- master_type = FORMAT_360K;
- else if(80 == Tr_per_dsk && 15 == Sectors_per_track)
- master_type = FORMAT_12MB;
- else if(9 == Sectors_per_track)
- master_type = FORMAT_720K;
- else if(18 == Sectors_per_track)
- master_type = FORMAT_144M;
- else
- {
- cprintf("\r\n Unrecognized disk type, can't format\r\n");
- goto exit_format;
- }
-
- my_dpb.spt = Sectors_per_track;
- call_17h = TRUE;
- switch(drive_type)
- {
- case FLOPPY_360K:
- my_dpb.gap = 0x2a; /* 5.25" disk */
- my_dpb.gap_l = 0x50;
- switch(master_type)
- {
- case FORMAT_360K:
- format_type = 1;
- goto set_format_type;
-
- case FORMAT_12MB:
- case FORMAT_720K:
- case FORMAT_144M:
- case FORMAT_X:
- goto mismatch;
- }
-
- case FLOPPY_12MB:
- my_dpb.gap = 0x2a; /* 5.25" disk */
- my_dpb.gap_l = 0x50;
- switch(master_type)
- {
- case FORMAT_360K:
- case FORMAT_X:
- format_type = 2;
- goto set_format_type;
-
- case FORMAT_12MB:
- format_type = 3;
- goto set_format_type;
-
- case FORMAT_720K:
- case FORMAT_144M:
- goto mismatch;
- }
- case FLOPPY_720K:
- my_dpb.gap = 0x1b; /* 3.5" disk */
- my_dpb.gap_l = 0x6c;
- switch(master_type)
- {
- case FORMAT_360K:
- case FORMAT_12MB:
- case FORMAT_144M:
- case FORMAT_X:
- goto mismatch;
-
- case FORMAT_720K:
- format_type = 4;
- goto set_format_type;
- }
- case FLOPPY_144M:
- my_dpb.gap = 0x1b; /* 3.5" disk */
- my_dpb.gap_l = 0x6c;
- call_17h = FALSE;
- switch(master_type)
- {
- case FORMAT_360K:
- case FORMAT_12MB:
- case FORMAT_X:
- goto mismatch;
-
- case FORMAT_144M:
- case FORMAT_720K:
- goto set_format_type;
- }
- }
-
- mismatch:
- cprintf(" \r\nDrive not capable of requested disk format\r\n\n");
- goto exit_format;
-
- set_format_type:
- if(0 != (0xff00 & biosdisk(0x18, int13_drive, 0, Tr_per_dsk,
- Sectors_per_track, 0, format_record)))
- {
- cprintf("\r\n biosdisk call with ah=18h failed\r\n\n");
- goto exit_format;
- }
-
- if(call_17h)
- {
- biosdisk(0x17, int13_drive, 0, 0, 0, format_type, format_record);
- if(0 != biosdisk(0x17, int13_drive, 0, 0, 0, format_type, format_record))
- {
- cprintf("\r\n biosdisk call with ah=17h failed\r\n\n");
- goto exit_format;
- }
- }
-
- if(Is_X && (int13_drive < 4))
- {
- poke(0x40, 0x90 + int13_drive, 0x54);
- my_dpb.gap = 0x1b; /* copied from 800kfmat.asm */
- my_dpb.gap_l = 0x28;
- }
-
- /* Use BIOS to format tracks
- */
- for(tr = 0; tr < Tr_per_dsk; tr++)
- {
- for(sd = 0; sd < Sd_per_dsk; sd++)
- {
- for(s = 0; s < Sectors_per_track; s++)
- {
- format_record[s].cyl = tr;
- format_record[s].head = sd;
- format_record[s].rec = s + 1;
- format_record[s].num = 2;
- }
- /* RX-50 disks have 2:1 interleave except for first two tracks
- */
- if(Is_X && 3 == X_type && tr > 1)
- {
- for(s = 0; s < Sectors_per_track; s+=2)
- {
- format_record[s].rec = s/2 + 1;
- format_record[s+1].rec = s/2 + 6;
- }
- }
- success = tries = 0;
- while (!success && (tries < 3))
- {
- if(0 == biosdisk(FORMAT_TRK, int13_drive, sd, tr, 1,
- Sectors_per_track, &format_record[0]))
- success = 1;
- else
- {
- biosdisk(RESET_CONTROLLER, 0, 0, 0, 0, 0, (void *)0);
- tries++;
- }
- }
- if (success)
- statline(2*(Tr_per_dsk - tr) - sd, FORMAT_INDICATOR);
- else
- {
- cprintf("\r\n Format Error - aborting...\r\n");
- exit(1);
- }
- }
- }
- status = NO_ERR;
- exit_format:
- /* Restore interrupt vector 0x1e. Perhaps doing this is unnecessary, but it
- seems best to leave things as we found them.
- */
- reset_disk_controller();
- return(status);
- }
-
- /*********************************************************************
- erase_temp_file - called when program terminates if the disk file used to
- hold the floppy image is to be erased.
- */
- void erase_temp_file(void)
- {
- remove(Tempfile);
- return;
- }
-
- /*********************************************************************
- disk_rw - read or write floppy using int 13h
- This routine uses 0-based sector numbering in calculations, then adjusts
- for 1-based sectors in the biosdisk call.
- */
-
- LOGICAL disk_rw(int int13_drive, int action, int first_sector, int nsects,
- char *buffer)
- {
- int hs, n_rw, retries, sector, side, status, track;
-
- if(Is_X && (0 == first_sector) && (int13_drive < 4))
- poke(0x40, 0x90 + int13_drive, 0x54);
-
- hs = Sd_per_dsk * Sectors_per_track;
- track = first_sector / hs;
- side = Sd_per_dsk > 1 ? (first_sector/Sectors_per_track) & 1 : 0;
- sector = first_sector - track*hs - side*Sectors_per_track;
- while(nsects > 0)
- {
- n_rw = Sectors_per_track - sector;
- n_rw = min(n_rw, nsects);
- retries = 0;
- do {
- status = biosdisk(action, int13_drive, side, track, sector+1, n_rw,
- buffer);
- }
- while (0 != status && ++retries < 3);
- if(0 != status)
- return(status);
- nsects -= n_rw;
- sector = 0;
- if(++side >= Sd_per_dsk)
- {
- side = 0;
- track++;
- }
- buffer += BYTES_PER_SECTOR * n_rw;
- }
- return(NO_ERR);
- }
-
- /* Called when we exit to reset things. If controller is not reset, some
- errors may leave floppy controller in a state where nothing works.
- */
- void reset_disk_controller(void)
- {
- setvect(0x1e, System_dpb);
- biosdisk(RESET_CONTROLLER, 0, 0, 0, 0, 0, (void *)0);
- return;
- }
-
- void disk_error_print(unsigned int error_code)
- {
- char *disk_errors[18]={"success", "invalid function",
- "address mark not found", "disk write-protected", "sector not found",
- "reset failed", "disk changed", "activity failed", "DMA overrun",
- "DMA across 64K boundary", "bad sector detected", "bad track detected",
- "unsupported track or invalid media",
- "invalid number of sectors on format",
- "control data address mark detected",
- "DMA arbitration level out of range",
- "uncorrectable CRC or ECC error on read", "data ECC corrected"};
-
- cprintf(" Disk error is ");
- if(0x20 == error_code)
- cprintf("controller failure");
- else if(0x40 == error_code)
- cprintf("seek failed");
- else if(0x80 == error_code)
- cprintf("timeout (not ready)");
- else if(error_code < 18)
- cprintf(disk_errors[error_code]);
- else cprintf("unknown error, code=%x", error_code);
- cprintf("\r\n");
- return;
- }
-